package grid;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.scene.Group;
import javafx.scene.control.ScrollBar;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import player.Player;

import java.util.Iterator;
import java.util.List;

/**
 * Date: 24.11.12
 *
 * @author Mikhail Larinskiy
 */
public class PlayerGrid {
    public static final double EM = 200;
    public static final double WIDTH_FACTOR = 4;
    public static final double HEIGHT_FACTOR = 3;

    public static final double SCENE_WIDTH = EM * WIDTH_FACTOR;
    public static final double SCENE_HEIGHT = EM * HEIGHT_FACTOR;

    private static final double SCROLL_BAR_WIDTH = 15;
    private static final double SCROLL_BAR_AMOUNT = 40;

    private static final int COLUMNS = 4;
    private static final double GAP = 15;
    private static final double INSETS = 5;

    private final Pane stage;
    private final List<Player> players;

    private final ScrollBar verticalScrollBar = new ScrollBar();

    private double playerHeight;
    private double playerWidth;

    public PlayerGrid(Pane stage, List<Player> players) {
        this.stage = stage;
        this.players = players;

        init();
    }

    private void init() {
        Group root = new Group();
        stage.getChildren().add(root);

        int rows = players.size() / COLUMNS;
        if (players.size() % COLUMNS != 0)
            rows++;

        boolean showScrollBar = initPlayerSize(rows);

        layoutPlayers(rows, INSETS);

        root.getChildren().addAll(players);

        if (showScrollBar) {
            layoutScrollBar(rows);
            root.getChildren().add(verticalScrollBar);
        }

        Rectangle bg = new Rectangle(SCENE_WIDTH, SCENE_HEIGHT, Color.BLACK);
        root.getChildren().add(bg);
        bg.toBack();
    }

    private void layoutPlayers(int rows, double scrollValue) {
        double nextPlayerX = INSETS;
        double nextPlayerY = INSETS + scrollValue;

        Iterator<Player> playerIterator = players.iterator();
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < COLUMNS; j++) {
                if (!playerIterator.hasNext())
                    break;

                Player nextPlayer = playerIterator.next();

                nextPlayer.setOnMouseClicked(new PlayerClickHandler(nextPlayer, nextPlayerX, nextPlayerY));

                nextPlayer.setSize(playerWidth, playerHeight);

                nextPlayer.setLayoutX(nextPlayerX);
                nextPlayer.setLayoutY(nextPlayerY);

                nextPlayerX += playerWidth + GAP;
            }

            nextPlayerX = INSETS;
            nextPlayerY += GAP + playerHeight;
        }
    }

    private void layoutScrollBar(final int rows) {
        verticalScrollBar.setLayoutY(0);
        verticalScrollBar.setLayoutX(SCENE_WIDTH - SCROLL_BAR_WIDTH);

        verticalScrollBar.setVisibleAmount(SCROLL_BAR_AMOUNT);
        verticalScrollBar.setPrefSize(SCROLL_BAR_WIDTH, SCENE_HEIGHT);

        int rowsPerPage = (int) (SCENE_HEIGHT / playerHeight);
        verticalScrollBar.setMax((playerHeight + GAP) * (rows - rowsPerPage));

        verticalScrollBar.setUnitIncrement(20);
        verticalScrollBar.setOrientation(Orientation.VERTICAL);

        verticalScrollBar.valueProperty().addListener(new ChangeListener<Number>() {
            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                layoutPlayers(rows, -verticalScrollBar.getValue());
            }
        });
    }

    private boolean initPlayerSize(int rows) {
        double extraHeight = (rows - 1) * GAP + INSETS * 2;
        double extraWidth = (COLUMNS - 1) * GAP + INSETS * 2;

        initPlayerWidth(extraWidth);
        initPlayerHeight();

        boolean showScrollBar = rows * playerHeight + extraHeight > SCENE_HEIGHT;
        if (showScrollBar) {
            extraWidth += SCROLL_BAR_WIDTH;
            initPlayerWidth(extraWidth);
            initPlayerHeight();
        }

        return showScrollBar;
    }

    private void initPlayerWidth(double extraWidth) {
        playerWidth = (SCENE_WIDTH - extraWidth) / COLUMNS;
    }

    private void initPlayerHeight() {
        playerHeight = playerWidth * HEIGHT_FACTOR / WIDTH_FACTOR;
    }

    private class PlayerClickHandler implements EventHandler<MouseEvent> {
        private final Player player;
        private final double defaultX;
        private final double defaultY;

        private boolean fullScreen;

        private PlayerClickHandler(Player player, double defaultX, double defaultY) {
            this.player = player;
            this.defaultX = defaultX;
            this.defaultY = defaultY;
        }

        public void handle(MouseEvent mouseEvent) {
            if (mouseEvent.getClickCount() < 2) {
                player.playPause();
                return;
            }

            for (Player otherPlayer : players) {
                if (player != otherPlayer) {
                    otherPlayer.setVisible(fullScreen);
                    verticalScrollBar.setVisible(fullScreen);
                }
            }

            double newX = fullScreen ? defaultX : INSETS;
            double newY = fullScreen ? defaultY : INSETS;
            double newWidth = fullScreen ? playerWidth : SCENE_WIDTH - INSETS * 2;
            double newHeight = fullScreen ? playerHeight : SCENE_HEIGHT - INSETS * 2;

            player.setLayoutX(newX);
            player.setLayoutY(newY);
            player.setSize(newWidth, newHeight);

            if (fullScreen) {
                player.toBack();
            }  else {
                player.toFront();
            }

            fullScreen = !fullScreen;
        }
    }
}
